题解:P16981 [NWERC 2017] 安装应用 / Installing Apps

907 字
5 分钟
题解:P16981 [NWERC 2017] 安装应用 / Installing Apps

题面传送门:P16981 [NWERC 2017] 安装应用 / Installing Apps

题目大意#

给出一些应用下载与存储的大小和手机容量,求最多安装应用的数量与方案。

思路讲解#

首先注意到题目中要素齐全:手机容量、物品占用空间等很难不把思路拉到 DP 中,尤其是背包问题。
但要注意,本题的容量会随放入物品而变化,因此在选择下载应用时也要有一定的策略,这个策略可以由贪心得到。
最后,不仅要输出拿取物品数量,还要在数量为正数时输出拿取方案。
综上,得任务列表如下:

  • 贪心策略对应用预处理
  • 动态规划得安装数量
  • 整合拿取路径
贪心

每个应用涉及值众多,此处建议用结构体存储。
我们可以将应用分为两类,分别为 d≤sd \le s 的应用与 d>sd > s 的应用。
对于第一种应用,更为重要的肯定是存储大小,为了安装的应用尽可能多,应将 ss 更小的应用放在前。
对于第二种应用,有多种排序方法。可以通过计算 dd 与 ss 的差值来排序,差值越大的排在前面。本方式可以推广到第一种应用上,因此排序规则为按 dd 与 ss 的差值降序排序。
由于排序方式众多,这里再展示官方的排序方法。

标准解法
bool cmp(App a, App b) {
if (a.d <= a.s && b.d > b.s) return true; // a is type 1, b is type 2
if (a.d > a.s && b.d <= b.s) return false;
if (a.d <= a.s && b.d <= b.s) return a.d < b.d; // Type 1: sort by d asc
return a.s > b.s; // Type 2: sort by s desc (or d desc)
}

此处策略为:如果安装后变大或不变,按 dd ‌升序排序。 如果安装后变小,按 ss 降序排序。
后文【代码实现】部分展示的是本文中给出的非官方解法。

背包 DP

排序完成后,定义 dpi,jdp_{i,j} 表示考虑前 ii 个应用,安装完 jj 个应用后,手机至少需要多大容量。目标是找到最大的 jj,使得 dpn,j≤cdp_{n,j} \le c。
若在状态转移时沿用此思路,则需计算选定应用空间之和的最小值,计算较复杂。所以可以改变数组定义,改为前 ii 个应用,安装完 jj 个应用后,手机剩余空间最大值。初始化 dp[0][0]=c,其他位置值为 −1-1,防止不剩空间与无法放入混淆。此时目标是找到最大的 jj,使得 dpn,j≥0dp_{n,j} \ge 0。
循环遍历每一个应用,分安装与否两种情况。转移方程如下:
dpi,j={max(dpi,j,dpi−1,j)(不安装)max(dpi,j,dpi−1,j−1−ai.s)(安装)dp_{i,j} = \begin{cases} max(dp_{i,j}, dp_{i-1,j}) &\text{(不安装)}\\ max(dp_{i,j}, dp_{i-1,j-1}-a_{i}.s) &\text{(安装)}\\ \end{cases}
注意 jj 可为 00,若安装应加条件 j>0j>0。

完成 DP 后,倒序循环找出拿取数量,将此数量记为 numnum。

路径整合

查找路径时可以在 DP 过程中,记录每个状态是由哪个状态转移而来(即选没选第 ii 个应用)。最后从最终状态 n,numn, num 反向回溯,即可得到安装了哪些应用,但是为倒序,需要倒序输出或翻转数组。

代码实现#

完整代码
#include<bits/stdc++.h>
using namespace std;
int n,c,dp[505][505],num;
bool b[505][505];
vector<int>plan;
struct data{
int d,s,m,del,pos;
}a[505];
bool cmp(data x,data y){
return x.del>y.del;
}
int main(){
cin>>n>>c;
for(int i=1;i<=n;i++){
cin>>a[i].d>>a[i].s;
a[i].m=max(a[i].d,a[i].s);
a[i].del=a[i].d-a[i].s;
a[i].pos=i;
}
sort(a+1,a+n+1,cmp);
memset(dp,-1,sizeof(dp));
dp[0][0]=c;
for(int i=1;i<=n;i++){
for(int j=0;j<=i;j++){
dp[i][j]=max(dp[i][j],dp[i-1][j]);
if(j>0&&dp[i-1][j-1]>=a[i].m&&dp[i-1][j-1]-a[i].s>dp[i][j]){
dp[i][j]=dp[i-1][j-1]-a[i].s;
b[i][j]=1;
}
}
}
for(int i=n;i>=0;i--){
if(dp[n][i]>=0){
num=i;
break;
}
}
cout<<num<<'\n';
if(num==0){
return 0;
}
for(int i=n,j=num;i>0&&j>0;i--){
if(b[i][j]==1){
plan.push_back(a[i].pos);
j--;
}
}
for(int i=plan.size()-1;i>=0;i--){
cout<<plan[i]<<' ';
}
return 0;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

题解:P16981 [NWERC 2017] 安装应用 / Installing Apps
https://zhedaotixuanbo.pages.dev/posts/题解:P16981 [NWERC 2017] 安装应用 Installing Apps/
作者
zhedaotixuanbo
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
zhedaotixuanbo
这道题选什么? _____!
公告
分类
标签
站点统计
文章
17
分类
1
标签
21
总字数
6,295
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
ZTXB v1.0.0
文章许可
CC BY-NC-SA 4.0